#pragma once

#include <string>
#include <sys/stat.h>
#include <random>
#include <iomanip>
#include <sstream>
#include <atomic>
#include <fstream>
#include <boost/algorithm/string.hpp>

//#include "log.hpp"
#include "../RPC/source/common/log.hpp"

namespace oj_util
{
    //using namespace oj_log;

    static const std::string temp_path = "./temp/";

    class PathUtil
    {
    public:
        // 编译时需要的文件
        static const std::string Src(const std::string &file_name)
        {
            return addSuffix(file_name, ".cc");
        }

        static const std::string Exe(const std::string &file_name)
        {
            return addSuffix(file_name, ".exe");
        }

        static const std::string CompileErr(const std::string &file_name)
        {
            return addSuffix(file_name, ".compile_err");
        }

        // 运行代码需要的文件
        static const std::string Stdin(const std::string &file_name)
        {
            return addSuffix(file_name, ".stdin");
        }

        static const std::string Stdout(const std::string &file_name)
        {
            return addSuffix(file_name, ".stdout");
        }

        static const std::string Stderr(const std::string &file_name)
        {
            return addSuffix(file_name, ".stderr");
        }

    private:
        static const std::string addSuffix(const std::string &file_name, const std::string &suffix)
        {
            std::string path_name = temp_path;
            path_name += file_name;
            path_name += suffix;

            return path_name;
        }
    };

    class StringUtil
    {
    public:
        //按分隔符seq分割字符串str，并将分割的结果保存到数组tokens
        //使用boost库进行字符串切割
        static void spiltStr(const std::string& str, std::vector<std::string>& tokens, const std::string& sep)
        {
            boost::split(tokens, str, boost::is_any_of(sep), boost::algorithm::token_compress_on);
        }
    };

    class FileUtil
    {
    public:
        static bool isFileExists(const std::string &path_name)
        {
            struct stat _stat;
            int ret = stat(path_name.c_str(), &_stat); // 获取文件状态
            return ret == 0;
        }

        static void removeFile(const std::string& path_name)
        {
            if (isFileExists(path_name))
                unlink(path_name.c_str());
        }

        //使用uuid生成唯一文件名
        static const std::string uniqueFileName()
        {
            // UUID: 8-4-4-4-12的32个16进制数

            std::stringstream ss; // 用于存放uuid的字符流

            // 1. 创建一个机器随机数对象
            std::random_device rd;
            // 2. 将机器随机数作为种子，生成伪随机对象
            std::mt19937 genenrator(rd());
            // 3. 限制随机数范围 [0, 255]
            std::uniform_int_distribution<int> distibution(0, 255);

            // 随机生成8个16进制随机数 num1num2num3num4-num5num6-num7num8
            for (int i = 0; i < 8; i++)
            {
                if (i == 4 || i == 6)
                    ss << '-';
                ss << std::setw(2) << std::setfill('0') << std::hex << distibution(genenrator);
            }

            ss << '-';

            static std::atomic<size_t> seq(1); // 序号，每次加一
            size_t cur = seq.fetch_add(1);     // 原子操作，保证线程安全

            // 还剩8个字节，每次取前1个字节（8位）
            for (int i = 7; i >= 0; --i)
            {
                if (i == 5)
                    ss << '-';
                ss << std::setw(2) << std::setfill('0') << std::hex << ((cur >> (i * 8)) & 0xff);
            }

            return ss.str();
        }

        static bool writeFile(const std::string &path_name, const std::string &content)
        {
            std::ofstream out(path_name);
            if (!out.is_open())
            {
                LOG(JsonMuduoRPC::ERROR, "%s: 文件打开失败", path_name.c_str());
                return false;
            }

            out.write(content.c_str(), content.size()); 

            out.close();
            return true;
        }   

        //keep：是否保留换行符
        static bool readFile(const std::string &path_name, std::string& content, bool keep = false)
        {
            std::ifstream in(path_name);
            if (!in.is_open())
            {
                LOG(JsonMuduoRPC::ERROR, "%s: 文件打开失败", path_name.c_str());
                return false;
            }

            content.clear();

            const char seq = (keep ? '\n' : ' ');
            std::string line;
            while (std::getline(in, line))
            {
                content += line;
                content += seq;
            }

            in.close();

            return true;
        }
    };
} // namespace oj_util
